package dawn.service.socket;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.filter.logging.MdcInjectionFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dawn.core.annotation.OP;
import dawn.core.dispatch.Invoker;
import dawn.core.dispatch.SingleListenerDispathcer;
import dawn.ioc.InvokerGenerator;
import dawn.net.Message;
import dawn.net.MessageType;
import dawn.net.RpcManager;
import dawn.net.RpcRequest;
import dawn.net.RpcResponse;
import dawn.server.GameServer;
import dawn.service.Service;

public class SocketService extends SingleListenerDispathcer<Integer, Object> implements Service {

	private static final Logger log = LoggerFactory.getLogger(SocketService.class);

	private int port = 8888;
	private NioSocketAcceptor acceptor;

	@Override
	public void init() {

		// 生成代理类
		List<Service> serviceList = GameServer.getIns().getServiceList();
		for (Service service : serviceList) {
			for (Method m : service.getClass().getDeclaredMethods()) {
				OP a = m.getAnnotation(OP.class);
				if (a != null) {
					Invoker<Object> invoker = InvokerGenerator.build(service, m);
					if (invoker != null)
						this.addListener(a.op(), invoker);
				}
			}
		}

		// 监听
		acceptor = new NioSocketAcceptor();
		DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();

		MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
		chain.addLast("mdc", mdcInjectionFilter);

		// 使用UTF-8编码，使用\n作分隔符
		TextLineCodecFactory codecFactory = new TextLineCodecFactory(Charset.forName("UTF-8"), LineDelimiter.CRLF,
				LineDelimiter.CRLF);
		chain.addLast("codec", new ProtocolCodecFilter(codecFactory));

		chain.addLast("logger", new LoggingFilter());

		// Bind
		SocketHandler sockethandler = new SocketHandler();
		acceptor.setHandler(sockethandler);
		acceptor.getSessionConfig().setReadBufferSize(2048);
		acceptor.setReuseAddress(true);
		try {
			acceptor.bind(new InetSocketAddress(port));
		} catch (IOException e) {
			e.printStackTrace();
		}
		log.info("Listening on {}", port);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void update() {
		Map<Long, IoSession> sessionMap = acceptor.getManagedSessions();
		for (IoSession session : sessionMap.values()) {
			Collection<SocketEvent> events = (Collection<SocketEvent>) session.getAttribute(SessionAttribute.MESSAGES);
			for (SocketEvent socketEvent : events) {
				Message msg = socketEvent.getMsg();
				if (msg.getType() == MessageType.MESSAGE) {

					dispatch(msg.getOpCode(), socketEvent);
				} else if (msg.getType() == MessageType.RPC_REQUEST) {

					Object ret = invoker(msg.getOpCode(), socketEvent);
					RpcRequest request = (RpcRequest) msg;
					RpcResponse response = new RpcResponse();
					response.setSequence(request.getSequence());
					response.setOpCode(request.getOpCode());
					response.setData(ret);

					session.write(response);
				} else if (msg.getType() == MessageType.RPC_RESPONSE) {

					RpcResponse response = (RpcResponse) msg;
					RpcManager.getInstance().dispatch(response.getSequence(), response);
				}
			}
		}
	}

	@Override
	public void shutdown() {
		if (acceptor != null && !acceptor.isDisposed())
			acceptor.dispose();
	}
}
